/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/graphics/dx11.1/details/common.hpp>
#include <baja/graphics/dx11.1/graphics_fwd.hpp>
#include <baja/graphics/dx11.1/details/enum_mappings.hpp>

namespace baja { namespace graphics { namespace dx11_1 {

class material : public imaterial 
{
private:
    std::shared_ptr<itexture> _texture[g::constants::maxTextures];
    color _ambientColor;
    color _diffuseColor;
    color _emissiveColor;
    color _specularColor;
    float32 _specularPower;
    bool _isDirty;

    struct constants {
        static const uint32 materialNameSize = 32;
    };

public:
    material()
    {
        _ambientColor.set(1.0f, 1.0f, 1.0f, 1.0f);
        _diffuseColor.set(1.0f, 1.0f, 1.0f, 1.0f);
        _emissiveColor.set(0.0f, 0.0f, 0.0f, 1.0f);
        _specularColor.set(1.0f, 1.0f, 1.0f, 1.0f);
        _specularPower = 16.0f;
        _isDirty = true;
    }

    static std::shared_ptr<material> create()
    {
        return std::make_shared<material>();
    }

    // get ambient color
    color getAmbientColor(void)
    {
        return _ambientColor;
    }

    // set ambient color
    void setAmbientColor(const color& color)
    {
        _ambientColor = color;
        _isDirty = true;
    }
    
    // set ambient color from a packed color value
    void setAmbientColorPacked(const packed_color color)
    {
        _ambientColor.setFromPackedColor(color);
        _isDirty = true;
    }
    
    // get diffuse color
    color getDiffuseColor()
    {
        return _diffuseColor;
    }
    
    // set diffuse color
    void setDiffuseColor(const color& color)
    {
        _diffuseColor = color;
        _isDirty = true;
    }
    
    // set diffuse color from a packed color value
    void setDiffuseColorPacked(const packed_color color) 
    {
        _diffuseColor.setFromPackedColor(color);
        _isDirty = true;
    }
    
    // get emissive color
    color getEmissiveColor()
    {
        return _emissiveColor;
    }
    
    // set emissive color
    void setEmissiveColor(const color& color)
    {
        _emissiveColor = color;
        _isDirty = true;
    }
    
    // set emissive color from a packed color value
    void setEmissiveColorPacked(const packed_color color) 
    {
        _emissiveColor.setFromPackedColor(color);
        _isDirty = true;
    }
    
    // get specular color
    color getSpecularColor()
    {
        return _specularColor;
    }
    
    // set specular color
    void setSpecularColor(const color& color)
    {
        _specularColor = color;
        _isDirty = true;
    }
    
    // set specular color from a packed color value
    void setSpecularColorPacked(const packed_color color) 
    {
        _specularColor.setFromPackedColor(color);
        _isDirty = true;
    }
    
    // get specular power of the material 
    float32 getSpecularPower()
    {
        return _specularPower;
    }
    
    // set specular power of the material 
    void setSpecularPower(const float32 specularPower)
    {
        BAJA_CHECK_ARG(specularPower >= 0);
        _specularPower = specularPower;
        _isDirty = true;
    }
    
    // gets the texture for this material
    std::shared_ptr<itexture> getTexture()
    {
        return _texture[0];
    }
    
    // gets the texture for this material
    std::shared_ptr<itexture> getTextureAtIndex(const uint32 index)
    {
        BAJA_CHECK_ARG(index < g::constants::maxTextures);

        return _texture[index];
    }
    
    // sets the texture for this material
    void setTexture(const std::shared_ptr<itexture>& texture)
    {
        _texture[0] = texture;
        _isDirty = true;
    }
    
    // sets the texture at specified index
    void setTextureAtIndex(const uint32 index, const std::shared_ptr<itexture>& texture)
    {
        BAJA_CHECK_ARG(index < g::constants::maxTextures);
        _texture[index] = texture;
        _isDirty = true;
    }
    
    // clones the material, returning a new material object via the clone parameter
    std::shared_ptr<imaterial> clone()
    {
        std::shared_ptr<material> newMaterial = material::create();

        for (uint32 i = 0; i < g::constants::maxTextures; i++)
        {
            newMaterial->_texture[i] = _texture[i];
        }
        newMaterial->_ambientColor = _ambientColor;
        newMaterial->_diffuseColor = _diffuseColor;
        newMaterial->_emissiveColor = _emissiveColor;
        newMaterial->_specularColor = _specularColor;
        newMaterial->_specularPower = _specularPower;
        newMaterial->_isDirty = true;

        return newMaterial;
    }
    
    // equality test
    bool equals(const std::shared_ptr<imaterial>& m) 
    {
        BAJA_CHECK_ARG(m.get());

        std::shared_ptr<material> that = std::dynamic_pointer_cast<material>(m);

        bool result = false;

        if (that != NULL)
        {
            bool tmpResult = (
                _ambientColor == that->_ambientColor &&
                _diffuseColor == that->_diffuseColor &&
                _emissiveColor == that->_emissiveColor &&
                _specularColor == that->_specularColor &&
                _specularPower == that->_specularPower
                );

            if (tmpResult)
            {
                for (uint32 i = 0; i < g::constants::maxTextures && tmpResult; i++)
                {
                    tmpResult = (_texture[i] == that->_texture[i]);
                }
            }

            result = tmpResult;
        }
        else
        {
            result = false;
        }

        return result;
    }
    
    // returns dirty flag
    bool getIsDirty()
    {
        return _isDirty;
    }

    // resets the _isDirty flag
    void resetIsDirty()
    {
        _isDirty = false;
    }
};

}}} // namespace baja.graphics.dx11_1

namespace baja { namespace graphics {

inline std::shared_ptr<imaterial> createMaterial()
{
    std::shared_ptr<baja::graphics::dx11_1::material> returnValue = baja::graphics::dx11_1::material::create();
    return std::dynamic_pointer_cast<imaterial>(returnValue);
}

}} // namespace baja.graphics 